package com.faner4cloud.yun.common.util.progress;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Stack;
import java.util.function.BiFunction;

/**
 * 进度抽象步骤类
 */
public abstract class AbstractTask {

	/**
	 * 步骤名
	 */
	private final String name;
	/**
	 * 步骤所占进度比重
	 */
	private final int rate;
	/**
	 * 步骤所占总进度百分比
	 */
	private BigDecimal ratePercent;

	/**
	 * 前置任务
	 */
	private final AbstractTask parent;

	/**
	 * 步骤执行过程中已完成数据量
	 */
	private long doneData;
	/**
	 * 步骤执行所需要完成的总数据量
	 */
	private long totalData;

	AbstractTask(int rate, AbstractTask parent, String name) {
		if (rate < 0) {
			throw new IllegalArgumentException("rate=" + rate + "步骤比率必须大于0");
		}
		this.rate = rate;
		this.parent = parent;
		this.name = name;
	}

	/**
	 * 获得 步骤所占总进度百分比
	 *
	 * @return BigDecimal
	 */
	BigDecimal getRatePercent() {
		return ratePercent;
	}

	/**
	 * 配置 步骤所占总进度百分比
	 *
	 * @param ratePercent 步骤所占总进度百分比
	 */
	void setRatePercent(BigDecimal ratePercent) {
		this.ratePercent = ratePercent;
	}

	/**
	 * 获得 进度步骤名
	 *
	 * @return String
	 */
	String getName() {
		return this.name;
	}

	/**
	 * 获得 步骤所占进度比重
	 *
	 * @return int
	 */
	int getRate() {
		return this.rate;
	}

	/**
	 * 标记步骤完成度
	 *
	 * @param done  已完成数
	 * @param total 总数
	 */
	public void mark(long done, long total) {
		if (total <= 0) {
			throw new IllegalArgumentException("total=" + total + "步骤进度标记总数必须大于0");
		}
		this.doneData = done > total ? total : done;
		this.totalData = total;
	}

	/**
	 * 获得当前步骤占总进度的完成百分比
	 *
	 * @return BigDecimal
	 */
	BigDecimal getCurrentPercent() {
		if (this.getTotalData() == 0) {
			return BigDecimal.ZERO;
		} else {
			BigDecimal r1 = BigDecimal.valueOf((double) this.getDoneData() / (double) this.getTotalData());
			r1 = r1.multiply(this.getRatePercent());
			return r1;
		}
	}

	private long getDoneData() {
		return this.doneData;
	}

	private long getTotalData() {
		return this.totalData;
	}

	AbstractTask getParent() {
		return this.parent;
	}

	/**
	 * 带 BiFunction 的抽象步骤类
	 *
	 * @param <I>
	 * @param <O>
	 */
	public static abstract class AbstractStepTask<I, O> extends AbstractTask {

		/**
		 * 执行的BiFunction
		 */
		private BiFunction<I, AbstractStepTask<?, ?>, O> func;

		AbstractStepTask(int rate, AbstractTask parent, String name, BiFunction<I, AbstractStepTask<?, ?>, O> func) {
			super(rate, parent, name);
			if (rate == 0) {
				throw new IllegalArgumentException("rate = " + rate + " 步骤比率必须大于 0");
			}
			this.func = func;
		}

		/**
		 * 执行方法
		 *
		 * @param i 入参
		 * @return O
		 * @throws Exception
		 */
		O process(I i) {
			return func.apply(i, this);
		}

		/**
		 * 创建下一步骤
		 *
		 * @param rate     步骤所占的进度比重
		 * @param taskName 步骤名
		 * @param func     步骤执行的方法
		 * @return Next
		 */
		public <G> Next<O, G> next(int rate, String taskName, BiFunction<O, AbstractStepTask<?, ?>, G> func) {
			Next<O, G> task = new Next<>(rate, this, func, taskName);
			return task;
		}

		/**
		 * 创建下一步骤，<b>步骤名默认为 {@link ProgressBuilder#TASKNAME_UNDEFINED}</b>
		 *
		 * @param rate 步骤所占的进度比重
		 * @param func 步骤执行的方法
		 * @return Next
		 */
		public <G> Next<O, G> next(int rate, BiFunction<O, AbstractStepTask<?, ?>, G> func) {
			return next(rate, TaskBuilder.TASKNAME_UNDEFINED, func);
		}
	}

	/**
	 * 准备数据步骤
	 *
	 */
	public static class Prepare<I, O> extends AbstractStepTask<I, O> {
		Prepare(int rate, BiFunction<I, AbstractStepTask<?, ?>, O> func, String taskName) {
			super(rate, null, taskName, func);
		}
	}

	/**
	 * 数据处理步骤
	 *
	 */
	public static class Next<I, O> extends AbstractStepTask<I, O> {
		private Next(int rate, AbstractTask parent, BiFunction<I, AbstractStepTask<?, ?>, O> func, String taskName) {
			super(rate, parent, taskName, func);
		}

		public List<AbstractTask> build() {
			Stack<AbstractTask> reversed = new Stack<>();
			AbstractTask task = this;
			while (Objects.nonNull(task)) {
				reversed.add(task);
				task = task.getParent();
			}
			Collections.reverse(reversed);
			return reversed;
		}
	}

}
